package com.jintian.smart.kernel.flowable.component.action.base;

import com.jintian.smart.kernel.common.util.StringUtil;
import com.jintian.smart.kernel.core.annotation.flow.FlowOperate;
import com.jintian.smart.kernel.core.flow.FlowAction;
import com.jintian.smart.kernel.core.flow.IProcessAction;
import com.jintian.smart.kernel.core.flow.ITaskOperation;
import com.jintian.smart.kernel.core.flow.ProcessControl;
import com.jintian.smart.kernel.core.orm.IBizTable;
import com.jintian.smart.kernel.flowable.context.DefaultProcessContext;
import com.jintian.smart.kernel.flowable.domain.model.FlowImpl;
import com.jintian.smart.kernel.flowable.engine.BaseProcessService;
import com.jintian.smart.kernel.flowable.engine.DefaultFlowRuntime;
import com.jintian.smart.kernel.module.Model;
import com.jintian.smart.kernel.orm.context.BizTableContextImpl;
import com.jintian.smart.kernel.orm.impl.BizTableImpl;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

public abstract class AbstractProcessAction<T> extends BaseProcessService implements IProcessAction<T> {
    @Override
    @Transactional
    @FlowOperate
    public T execute(String processInstanceId, ProcessControl processControl) throws Exception {
        TaskEntity task = this.getPrincipalTask(processInstanceId, FlowAction.getFlowOwner());
        if (processControl == null) {
            ProcessInstance pi = this.runtimeService().createProcessInstanceQuery().includeProcessVariables().processInstanceId(task.getProcessInstanceId()).singleResult();
            processControl = new ProcessControl();
            processControl.setProcess(pi.getProcessDefinitionKey());
            processControl.setBusinessKey(pi.getBusinessKey());
        }
        this.checkTask(task, FlowAction.getFlowOwner(), this.mustCheck(processControl));

        return innerExecute(task, processControl);
    }

    protected abstract T innerExecute(TaskEntity task, ProcessControl processControl) throws Exception;

    protected TaskCheckEnum[] mustCheck(ProcessControl processControl) {
        if (processControl.isNoCheckOwner()) {
            return new TaskCheckEnum[]{TaskCheckEnum.Active};
        }
        return BaseProcessService.DEFAULT_TASK_CHECK;
    }

    @Override
    public ProcessControl queryProcessControl(String taskId, Map<String, Object> transientVariables) throws Exception {
        Map<String, Object> context = new HashMap<>();
        if (transientVariables != null) {
            context.putAll(transientVariables);
        }
        Task task = this.loadTask(taskId);
        String activity = task.getTaskDefinitionKey();
        ProcessInstance pi = this.runtimeService().createProcessInstanceQuery().includeProcessVariables()
                .processInstanceId(task.getProcessInstanceId()).singleResult();
        String bizClass = (String) pi.getProcessVariables().get(FlowAction.VARNAME_START_BIZCLASS);
        String modelName = (String) pi.getProcessVariables().get(FlowAction.VARNAME_START_MODELNAME);

        ProcessControl processControl = new ProcessControl();
        processControl.setProcess(pi.getProcessDefinitionKey());
        processControl.setBusinessKey(pi.getBusinessKey());
        processControl.setTransientVariables(context);

        if (!context.containsKey(ITaskOperation.FORMURL_PARAM_PROCESS)) {
            context.put(ITaskOperation.FORMURL_PARAM_PROCESS, pi.getProcessDefinitionKey());
        }

        if (!context.containsKey(ITaskOperation.FORMURL_PARAM_BUSINESS_KEY)) {
            context.put(ITaskOperation.FORMURL_PARAM_BUSINESS_KEY, pi.getBusinessKey());
        }
        if (!context.containsKey(ITaskOperation.FORMURL_PARAM_PROCESSINSTANCEID)) {
            context.put(ITaskOperation.FORMURL_PARAM_PROCESSINSTANCEID, pi.getProcessInstanceId());
        }

        if (!context.containsKey(ITaskOperation.FORMURL_PARAM_ACTIVITY)) {
            context.put(ITaskOperation.FORMURL_PARAM_ACTIVITY, activity);
        }
        if (!context.containsKey(ITaskOperation.FORMURL_PARAM_ACTIVITYLABEL)) {
            context.put(ITaskOperation.FORMURL_PARAM_ACTIVITYLABEL, task.getName());
        }
        if (!context.containsKey(ITaskOperation.FORMURL_PARAM_TASKID)) {
            context.put(ITaskOperation.FORMURL_PARAM_TASKID, task.getId());
        }
        context.put(FlowAction.TRANSIENT_VARNAME_OPERATOR, this.operatorProvider.currentOperator());
        FlowImpl flow = this.flowLoader.load(modelName, pi.getProcessDefinitionKey(), pi.getProcessDefinitionVersion());

        ClassLoader classLoader = null;
        if (modelName != null && this.getApplicationContext().getBean("model$" + modelName, Model.class) != null) {
            Model model = this.getApplicationContext().getBean("model$" + modelName, Model.class);
            classLoader = model.getClassLoader();
        } else {
            classLoader = this.getApplicationContext().getClassLoader();
        }
        // 加载业务数据
        Class<?> bizBeanClass = classLoader.loadClass(bizClass);
        String idAttr = this.bizTableLoader.getPrimaryKey(bizBeanClass);

        // 设置业务主表的过滤条件
        for (IBizTable table : flow.getBizTables()) {
            if (table.getEntityClass().equals(bizClass)) {
                if (StringUtil.isBlank(table.getCondition())) {
                    ((BizTableImpl) table).setCondition(idAttr + "= #{" + ITaskOperation.FORMURL_PARAM_BUSINESS_KEY + "}");
                }
                break;
            }
        }

        Map<String, Object> loadDataVariables = new HashMap<>(context);
        loadDataVariables.put(idAttr, context.get(ITaskOperation.FORMURL_PARAM_BUSINESS_KEY));
        @SuppressWarnings("rawtypes")
        Map<String, List> allData = this.bizTableLoader.loadBizTable(classLoader, flow.getBizTables(), loadDataVariables);
        // 添加业务数据访问环境，用于自定义函数获取业务数据
        context.put("bizData", new BizTableContextImpl(allData));

        // 业务规则
        DefaultFlowRuntime runtime = new DefaultFlowRuntime(flow, this.managementService(), context);
        try (DefaultProcessContext processContext = (DefaultProcessContext) this.processContextProvider.createInstance(
                pi.getProcessDefinitionKey(), task.getTaskDefinitionKey(), this.getAction(), pi.getProcessInstanceId(),
                task.getId(), pi.getBusinessKey(), processControl);) {
            // 1.压入流程环境
            runtime.getContext().put("context", processContext);
            // 2.检查业务规则
            List<Object> checkResult = runtime.checkRule(task.getTaskDefinitionKey(), this.getAction());
            if (checkResult.size() > 0) {
                processControl.putBizVariables("ruleResult", checkResult);
                if (runtime.existStopRule(checkResult)) {
                    processControl.putBizVariables("stop", true);
                    return processControl;
                }
            }

            // 计算流程变量
            Map<String, String> variables = flow.getContextVariables();
            for (Entry<String, String> e : variables.entrySet()) {
                if (!context.containsKey(e.getKey())) {
                    context.put(e.getKey(), runtime.calculate(e.getValue()));
                }
            }
        }
        return processControl;
    }
}
